package game;

import java.util.ArrayList;

import command.KnightsTourMoveCommand;
import command.NoSecondaryCommand;
import command.PiecePlaceCommand;

import view.KnightsTourTestView;

import chess.Board;
import chess.KnightPiece;
import chess.Piece;
import chess.Player;
import chess.Tile;

/**
 * Max Joyce 
 * -created Class(Jan 22,2010)
 * Andrew Jeffery
 *-Created Constructor(01/22/10)
 */
public class KnightsTourGame extends Game {
	
	private Board myBoard;
	private Player p;
	private boolean isNewGame;
	private Tile currentTile;
	private ArrayList<Tile> movementTiles;
	private boolean isMoveSet;
	
	public static final int MOVES_TO_WIN = 64;  //Every tile except for the one you are on
	
	private MoveExecutionList pastList;
	/**
	 * Default constructor for a normal game
	 * 
	 */
	public KnightsTourGame() {
		myBoard= new Board(this);
		p = new Player("Player",this,"Black");
		listOfMoves = new MoveExecutionList();
		isMoveSet = false;
	}
	
	/**
	 * Constructor that can be used if you want to specify your own player (Eg. If 
	 * you wanted to use a TestPlayer for a JUnit test)
	 */
	public KnightsTourGame(Player p){
		myBoard = new Board(this);
		this.p=p;
		listOfMoves = new MoveExecutionList();
		isMoveSet = false;
	}
	

	@Override
	/**
	 * This function will set up the game to be played.  In this case,
	 * a game of Knight's Tour
	 * <pre>
	 * Max Joyce
	 * -Creation (Jan 22,2010)
	 * -Implemented (Jan 26, 2010)
	 * </pre>
	 */	
	public void startup() {
		this.getBoard().drawBoard();
	}
	
	/**
	 * This determines how many moves you have made
	 * <pre>
	 * Max Joyce
	 * -No longer uses a for each loop to iterate through, since that
	 *  was causing an error (Jan 25,2010)
	 * </pre>
	 */
	public int getNumberOfMoves(){
		return listOfMoves.size();
	}

	/**
	 * This lets a (human) player take a turn
	 * 
	 * <pre>
	 * Max Joyce
	 * -Creation (Feb 9, 2010)
	 * </pre>
	 */	
	public void takeTurn(Player p){
		Tile newTile;
		
		Piece knight = p.getMyPieces().get(0); //Get the knight
		
		currentTile=knight.getCurrentTile();
		
		movementTiles = knight.getMovementTiles();
		filterMovementTiles(movementTiles); //Filter out tiles with pieces

		view.showPossibleMoves(movementTiles,currentTile);
	
		//System.out.println("Choose your destination");
		newTile=chooseTile();
		if(newTile == null){
			return;
		}
		if(!isMoveSet){
			if (!movementTiles.contains(newTile)){ //If the selected tile isn't a valid location 
				while (!movementTiles.contains(newTile)){
					newTile=chooseTile(); //Keep trying to get a valid tile
					if(newTile == null){
						return;
					}
				}
			}
			
			//Create the new move
			command.KnightsTourMoveCommand newMove = new command.KnightsTourMoveCommand(currentTile,newTile);
			newMove.execute();
			newMove.addSecondaryCommand(new NoSecondaryCommand());
			listOfMoves.add(newMove); //Add this move to the list of moves made
			currentTile=newTile; //Set the currentTile to the knights new position
		}else {
			isMoveSet = false;
		}

	}
	
	/**
	 * This function will modify the list of movement tiles to remove 
	 * spots with pieces
	 * 
	 * <pre>
	 * Max Joyce
	 * -Creation (Feb 9,2010)
	 * </pre>
	 */	
	
	public void filterMovementTiles(ArrayList<Tile> movementTiles){
		ArrayList<Tile> removeTiles = new ArrayList<Tile>();
		
		for (Tile t: movementTiles){
			if (t.isPieceHere()){
				//prepare to remove the tile
				removeTiles.add(t);
				
			}
		}
		//Remove the tiles that need to be removed.
		for (Tile t:removeTiles){
			movementTiles.remove(t);
		}
	}
	
	/**
	 * Implement the abstract play method from Game
	 * <pre>
	 * Samual Martelli
	 * -Creation (Jan 25,2010)
	 * Max Joyce
	 * -Started implementation (Jan 26,2010)
	 * </pre>
	 */
	@Override
	public void play() {
		
		ArrayList<Tile> movementTiles = null;
		this.view.showOutputMessage("Please enter your starting position:");
		Tile temp=chooseTile();
		
		if(temp == null){
			return;
		}
		KnightPiece knight = null;
		if(!isMoveSet){
			currentTile=temp; //Starting tile chosen
			command.Command comm = new PiecePlaceCommand(currentTile,"Knight",p);
			listOfMoves.add(comm);
			comm.execute();
			comm.addSecondaryCommand(new NoSecondaryCommand());
			knight = (KnightPiece)((command.PiecePlaceCommand)comm).getCurrentTile().getPieceHere();
			currentTile.setPieceHere(knight);
			 //Initialize movementTiles
		}else{
			knight = (KnightPiece)currentTile.getPieceHere();
		}
		movementTiles=currentTile.getPieceHere().getMovementTiles();
		
		super.getBoard().drawBoard(); //Redraw board
		while (!movementTiles.isEmpty() && !isNewGame){
			if(!isMoveSet){
				this.p.takeTurn();	
			}else{
				isMoveSet = false;
			}
			//Recalculate possible movement tiles
			movementTiles=((KnightPiece)currentTile.getPieceHere()).getMovementTiles();
			filterMovementTiles(movementTiles);
			this.getBoard().drawBoard(); //Redraw board
			
		}
		if (getNumberOfMoves()!=MOVES_TO_WIN && !isNewGame){
			this.view.showOutputMessage("Game over");	
		}
		else if(!isNewGame){
			this.view.showOutputMessage("Congratulations!  You have finished the Knight's Tour!");
		}
		
		
	}
	
	/**
	 * This method will redo current moves in a game
	 * <pre>
	 * Caleb Chan
	 * </pre>
	 */
	public void redo(){
		if (listOfMoves.redo()){
			currentTile=((command.KnightsTourMoveCommand)listOfMoves.getLastCommand()).getDestTile();
			view.displayBoard();
			movementTiles=currentTile.getPieceHere().getMovementTiles();
			filterMovementTiles(movementTiles);
			view.showPossibleMoves(movementTiles,currentTile);
		}

	}
	
	/**
	 * This method will undo current moves in a game
	 * <pre>
	 * Caleb Chan
	 * </pre>
	 */
	public void undo(){
		command.Command lastCommand=listOfMoves.getLastCommand();  //Get the last command before it is undone
		if(listOfMoves.getLastCommand() instanceof command.KnightsTourMoveCommand){	
			if (listOfMoves.undo()){
				currentTile=((command.KnightsTourMoveCommand)lastCommand).getOrigTile();  //The move has been undone, so it is in the original tile
				view.displayBoard();
				
				movementTiles=currentTile.getPieceHere().getMovementTiles();
				filterMovementTiles(movementTiles);
				view.showPossibleMoves(movementTiles,currentTile);
			}
		}
		
	}
	/**
	 * This will enact all task that need to be done before a new game can be created
	 */
	public void newGameSelected(){
		isNewGame = true;
	}
	/**
	 * This will set the player by the index. 
	 * @param player The index of the player in the current list of players
	 */
	@Override
	public void setPlayer(int player) {
	}
	/**
	 * This method will reset the board to the start position
	 * <pre>
	 * Caleb Chan
	 * </pre>
	 */
	@Override
	public void resetBoard(){
		super.resetBoard();
		pastList = listOfMoves;
		listOfMoves = new MoveExecutionList();
		currentTile = null;
		p.resetMyPieces();
	}
	@Override
	public void restoreBoard(){
		p.resetMyPieces();
		//System.out.println("List : \n" +pastList.toString());
		while(pastList.redo());
		setMoveList(pastList);
		/*listOfMoves = pastList;
		setCurrentTile(pastTile);
		chess.Piece piece = currentTile.getPieceHere();
		p.getMyPieces().get(0).getCurrentTile().setPieceHere(piece);*/
		//System.out.println("List : \n" +pastList.toString());
		//System.out.println("Player List : " + p.getMyPieces().toString());
	}
	/**
	 * This method will set the current tile of the knight
	 * @param t The tile that the knight is on
	 */
	private void setCurrentTile(Tile t){
		currentTile = t;
	}
	/**
	 * This method will do all the final placement of the knight on the board once it
	 * has been loading from a file
	 */
	private void tileSet(){
		chess.Piece piece = p.getMyPieces().get(0).getCurrentTile().getPieceHere();
		p.getMyPieces().get(0).getCurrentTile().setPieceHere(piece);
		setCurrentTile(piece.getCurrentTile());
	}
	/**
	 * This method will set the list of moves and it is used when importing a move list. 
	 * If the board has a game currently active, it will reset the board.
	 * @param list The move list that is being replaced
	 * <pre>
	 * Caleb Chan
	 * </pre>
	 */
	public void setMoveList(MoveExecutionList list) {
		listOfMoves = list;
		isMoveSet = true;
		tileSet();
	}
	/**
	 * This method will get the players in the game
	 * @return Returns a list of players
	 */
	@Override
	public ArrayList<Player> getPlayers() {
		ArrayList<Player> players = new ArrayList<Player>();
		players.add(p);
		return players;
	}
}
